use super::AwaitHandle;
use crate::runtime::{Aborted, Attr, Exit, FnOnceFuture, RawTaskContext, SetAborted};
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};

/// 启动一个全新的异步执行任务. 本函数可以在异步环境中调用，也可以在同步环境中调用.
/// 和当前线程并发且结束时间不确定，因此有Send和'static要求.
/// 如果同时创建多个异步任务，建议使用JoinSet, 可以更高效的等待全部任务或者某个任务结束.
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_with<T>(future: T, attr: &Attr) -> AwaitHandle<T::Output>
where
    T: Future + Send + 'static,
    T::Output: Send + 'static,
{
    TaskSpawn {
        future: Some(future),
        attr: attr.clone(),
    }
    .await
}

/// 参见local::spawn_with, attr为Attr::default()
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn<T>(future: T) -> AwaitHandle<T::Output>
where
    T: Future + Send + 'static,
    T::Output: Send + 'static,
{
    spawn_with(future, &Attr::default()).await
}

/// 参见local::spawn_fn_with, attr为Attr::default()
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_fn<F, R>(f: F) -> AwaitHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    spawn(FnOnceFuture::new(f)).await
}

/// 将已有的同步函数包装为异步任务执行体.
/// 注意此函数中不要调用阻塞函数，如果必须执行阻塞函数，可以利用attr交给另一个运行时实例来完成.
/// 比如: spawn_fn_with(block_function, &Attr::new(BLOCK_RUNTIME_ID))
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_fn_with<F, R>(f: F, attr: &Attr) -> AwaitHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    spawn_with(FnOnceFuture::new(f), attr).await
}

/// 启动一个全新的异步执行任务，本函数只能在异步环境中调用，否则JoinHandle::join返回Err.
/// 因为不存在和当前线程的并发，只是任务结束时间不定，因此只有'static生命周期要求，无Send要求.
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_local_with<T>(future: T, attr: &Attr) -> AwaitHandle<T::Output>
where
    T: Future + 'static,
    T::Output: 'static,
{
    TaskSpawnLocal {
        future: Some(future),
        attr: attr.clone(),
    }
    .await
}

/// 参见spawn_local_with, attr为Attr::default()
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_local<T>(future: T) -> AwaitHandle<T::Output>
where
    T: Future + 'static,
    T::Output: 'static,
{
    spawn_local_with(future, &Attr::default()).await
}

/// 将已有的同步函数包装为异步任务执行体,在当前调度线程执行.
/// 注意函数中不应该有阻塞调用，否则会阻塞当前线程其他任务的执行.
/// 参见spawn_local_with
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_fn_local_with<F, R>(f: F, attr: &Attr) -> AwaitHandle<R>
where
    F: FnOnce() -> R + 'static,
    R: 'static,
{
    spawn_local_with(FnOnceFuture::new(f), attr).await
}

/// 参见spawn_fn_local, attr为Attr::default()
/// 注意: 忽略Attr::id，如需要在指定运行时，使用runtime::spawn系列.
pub async fn spawn_fn_local<F, R>(f: F) -> AwaitHandle<R>
where
    F: FnOnce() -> R + 'static,
    R: 'static,
{
    spawn_local(FnOnceFuture::new(f)).await
}

/// 设置强制退出当前task的标志.
/// 如果Task最终返回Pending，则会被强制终止，终止之前会被调用一次，此时task_aborted().await返回true.
pub async fn exit() {
    Exit.await
}

/// 当前task是否被提前终止，提前终止有两种情况，一种是异步任务内部调用task_exit().await, 一种是JoinHandle::abort().
/// 注意：如果JoinHandle::abort()发生在首次调度之前，则异步任务被直接放弃，不被执行.
pub async fn aborted() -> bool {
    Aborted.await
}

/// 此设置后，后续的异步函数内部task_aborted().await会返回设置的值.
/// 如果发生了task切换，此状态会被丢失.
/// 如果期望退出当前task，请使用task_exit().await
pub async fn set_aborted(aborted: bool) {
    SetAborted(aborted).await
}

struct TaskSpawn<T> {
    future: Option<T>,
    attr: Attr,
}

impl<T> Unpin for TaskSpawn<T> {}

impl<T> Future for TaskSpawn<T>
where
    T: Future + Send + 'static,
    T::Output: Send + 'static,
{
    type Output = AwaitHandle<T::Output>;
    fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
        match ctx.spawn(self.future.take().unwrap(), &self.attr) {
            Ok(task) => Poll::Ready(AwaitHandle::<T::Output>::new(task)),
            Err(_) => Poll::Ready(AwaitHandle::<T::Output>::null()),
        }
    }
}

struct TaskSpawnLocal<T> {
    future: Option<T>,
    attr: Attr,
}

impl<T> Unpin for TaskSpawnLocal<T> {}

impl<T> Future for TaskSpawnLocal<T>
where
    T: Future + 'static,
    T::Output: 'static,
{
    type Output = AwaitHandle<T::Output>;
    fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
        match ctx.spawn_local(self.future.take().unwrap(), &self.attr) {
            Ok(task) => Poll::Ready(AwaitHandle::<T::Output>::new(task)),
            Err(_) => Poll::Ready(AwaitHandle::<T::Output>::null()),
        }
    }
}
